﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO;

using System.Windows.Forms.DataVisualization.Charting;
using VehicleTrajectoryApp;

namespace WindowsFormsApplication1
{
    public partial class ODStatisticsChartForm : Form
    {
        public CriticalOD odData = null;

        public int NumOfScenarios = 0;
        public string[] ScenarioNames = null;
        public double[] ScenarioWeights = null;
        public Dictionary<int, List<IVehicle>> vehicleMap = null;
        public int AnalysisStartHour = 6;
        public int AnalysisEndHour = 7;
        public MainForm parentForm = null;

        public int origin_zone_id;
        public int destination_zone_id;

        public ODStatisticsChartForm()
        {
            InitializeComponent();
        }

        private void ODStatisticsCharForm_Load(object sender, EventArgs e)
        {
            chart1.Location = new Point(0, 0);
            chart1.Size = this.ClientSize;

            string summaryText = string.Empty;
            summaryText += "Origin_Zone_ID,Destination_Zone_ID\n";
            summaryText += String.Format("{0},{1}\n", origin_zone_id, destination_zone_id);

            if (odData != null)
            {
                List<float>[] travelTimeByScenarios = new List<float>[this.NumOfScenarios];

                Dictionary<int, ODPathSummary> odSummaryMap = odData.odSummaryMap;

                for (int hour = this.AnalysisStartHour; hour < AnalysisEndHour; hour++)
                {
                    if (odSummaryMap.ContainsKey(hour))
                    {
                        summaryText += "\nStart_Hour:" + hour.ToString() + ",\n";
                        ODPathSummary summary = odSummaryMap[hour];
                        for (int scenario = 0; scenario < this.NumOfScenarios; scenario++)
                        {
                            summaryText += "Scenario " + scenario + ": " + this.ScenarioNames[scenario] + ",\n";
                            for (int pathIndex = 0; pathIndex < summary.pathList.Count; pathIndex++)
                            {
                                if (travelTimeByScenarios[scenario] == null)
                                {
                                    travelTimeByScenarios[scenario] = new List<float>();
                                }
                                if (summary.ScenarioBasedExperiencedPathTravelTimeMap[scenario].ContainsKey(pathIndex))
                                {                                    
                                    travelTimeByScenarios[scenario].AddRange(summary.ScenarioBasedExperiencedPathTravelTimeMap[scenario][pathIndex]);

                                    summaryText += string.Join(",",summary.ScenarioBasedExperiencedPathTravelTimeMap[scenario][pathIndex].ToArray()) +  ",";
                                }
                            }
                            summaryText += "\n";
                        }
                    }
                }

                chart1.ChartAreas.Clear();

                chart1.ChartAreas.Add("PDF");
                chart1.ChartAreas.Add("Combined");

                chart1.ChartAreas["PDF"].AxisX.Title = "Travel Time: (min)\n\nScenario Specific Travel Time Histogram";
                chart1.ChartAreas["PDF"].AxisY.Title = "Frequency";

                chart1.Legends.Clear();

                double[] pdfScale = new double[10];
                double[][] pdfCounts = new double[this.NumOfScenarios][];
                for (int scenario = 0; scenario < this.NumOfScenarios; scenario++)
                {
                    pdfCounts[scenario] = new double[10];
                }

                double min = double.MaxValue, max = double.MinValue;

                for (int scenario = 0; scenario < this.NumOfScenarios; scenario++)
                {
                    if (travelTimeByScenarios[scenario] != null && travelTimeByScenarios[scenario].Count > 0)
                    {
                        if (min > travelTimeByScenarios[scenario].Min())
                        {
                            min = travelTimeByScenarios[scenario].Min();
                        }

                        if (max < travelTimeByScenarios[scenario].Max())
                        {
                            max = travelTimeByScenarios[scenario].Max();
                        }
                    }
                }

                if (vehicleMap != null)
                {
                    foreach (KeyValuePair<int, List<IVehicle>> kvp in vehicleMap)
                    {
                        foreach (IVehicle v in kvp.Value)
                        {
                            if (v.VehicleTravelTime > max)
                            {
                                max = v.VehicleTravelTime;
                            }

                            if (v.VehicleTravelTime < min)
                            {
                                min = v.VehicleTravelTime;
                            }
                        }
                    }
                }

                chart1.ChartAreas["PDF"].AxisX.Minimum = 0;

                double unit_value = (max - min) / 10;

                for (int i = 0; i < 10; i++)
                {
                    pdfScale[i] = min + i * unit_value;
                }

                for (int sceneario = 0; sceneario < this.NumOfScenarios; sceneario++)
                {
                    string legendName;
                    if (this.NumOfScenarios > 1)
                    {
                        legendName = String.Format("{0}-{1}%", this.ScenarioNames[sceneario], this.ScenarioWeights[sceneario] * 100);
                    }
                    else
                    {
                        legendName = String.Format("{0}%", this.ScenarioNames[sceneario]);
                    }
                    Series series = chart1.Series.Add(legendName);

                    chart1.Legends.Add(new Legend(legendName));
                    series.Legend = legendName;
                    chart1.Legends[legendName].DockedToChartArea = "PDF";
                    series.ChartType = SeriesChartType.Column;
                    series.ChartArea = "PDF";

                    chart1.Legends[legendName].Docking = Docking.Top;
                    chart1.Legends[legendName].Alignment = StringAlignment.Far;

                    if (sceneario == 0)
                    {
                        series.Color = Color.Green;
                    }

                    if (sceneario == 1)
                    {
                        series.Color = Color.Yellow;
                    }

                    if (sceneario == 2)
                    {
                        series.Color = Color.Red;
                    }

                    series.BorderWidth = 3;

                    for (int n = 0; n < travelTimeByScenarios[sceneario].Count; n++)
                    {
                        int index = 0;
                        if (unit_value != 0)
                        {
                            index = (int)((travelTimeByScenarios[sceneario][n] - min) / unit_value);
                        }
                        if (index >= 10)
                        {
                            index = 9;
                        }
                        pdfCounts[sceneario][index]++;
                    }

                    for (int n = 0; n < 10; n++)
                    {
                        series.Points.Add(new DataPoint(pdfScale[n], pdfCounts[sceneario][n]));
                    }
                }

                double[] gps_pdfCounts = new double[10];
                if (vehicleMap != null)
                {
                    string legendName = "GPS Trajectories";
                    summaryText += "\n" + legendName + "\n";

                    Series series = chart1.Series.Add(legendName);

                    chart1.Legends.Add(new Legend(legendName));
                    series.Legend = legendName;
                    chart1.Legends[legendName].DockedToChartArea = "PDF";
                    series.ChartType = SeriesChartType.Column;
                    series.ChartArea = "PDF";

                    chart1.Legends[legendName].Docking = Docking.Top;
                    chart1.Legends[legendName].Alignment = StringAlignment.Far;

                    series.Color = Color.Chocolate;
                    series.BorderWidth = 3;

                    Dictionary<int, List<float>> gpsTravelTimeMap = new Dictionary<int, List<float>>();

                    foreach (KeyValuePair<int, List<IVehicle>> kvp in vehicleMap)
                    {
                        foreach (IVehicle v in kvp.Value)
                        {
                            if (v.VehicleStartHour >= AnalysisStartHour && v.VehicleStartHour < AnalysisEndHour)
                            {
                                if (!gpsTravelTimeMap.ContainsKey(v.VehicleStartHour))
                                {
                                    gpsTravelTimeMap[v.VehicleStartHour] = new List<float>();
                                }

                                gpsTravelTimeMap[v.VehicleStartHour].Add(v.VehicleTravelTime);

                                int index;
                                if (unit_value < 0.00001)
                                {
                                    index = 0;
                                }
                                else
                                {
                                    index = (int)((v.VehicleTravelTime - min) / unit_value);
                                }
                                if (index >= 10)
                                {
                                    index = 9;
                                }
                                gps_pdfCounts[index]++;
                            }
                        }
                    }

                    for (int hour = AnalysisStartHour; hour < AnalysisEndHour; hour++)
                    {
                        if (gpsTravelTimeMap.ContainsKey(hour))
                        {
                            summaryText += "Start Hour: " + hour.ToString() + "\n";
                            summaryText += string.Join(",", gpsTravelTimeMap[hour].ToArray()) + "\n";
                        }
                    }

                    summaryText += "\n";

                    for (int n = 0; n < 10; n++)
                    {
                        series.Points.Add(new DataPoint(pdfScale[n], gps_pdfCounts[n]));
                    }
                }


                //Combined PDF
                chart1.ChartAreas["Combined"].AxisX.Minimum = 0.0;
                Series combinedPDFSeries = chart1.Series.Add("Combined PDF");
                combinedPDFSeries.ChartType = SeriesChartType.Column;
                combinedPDFSeries.ChartArea = "Combined";
                combinedPDFSeries.YAxisType = AxisType.Primary;
                chart1.ChartAreas["Combined"].AxisX.Title = "Travel Time: (min)\n\nCombined Travel Time Histogram";
                chart1.ChartAreas["Combined"].AxisY.Title = "Frequency";
                chart1.ChartAreas["Combined"].AxisX.Minimum = 0;
                chart1.Legends.Add(new Legend("Combined PDF"));
                combinedPDFSeries.Legend = "Combined PDF";
                chart1.Legends["Combined PDF"].DockedToChartArea = "Combined";
                chart1.Legends["Combined PDF"].Docking = Docking.Top;
                chart1.Legends["Combined PDF"].Alignment = StringAlignment.Near;

                combinedPDFSeries.Color = Color.Blue;
                //combinedPDFSeries.BorderWidth = 3;


                double[] combinedPdf = new double[10];
                for (int n = 0; n < 10; n++)
                {
                    for (int scenario = 0; scenario < this.NumOfScenarios; scenario++)
                    {
                        combinedPdf[n] += pdfCounts[scenario][n] * this.ScenarioWeights[scenario];
                    }
                    combinedPDFSeries.Points.Add(new DataPoint(pdfScale[n], combinedPdf[n]));
                }

                //Combined CDF
                //chart1.ChartAreas["Combined PDF"].AxisX2.Minimum = (int)min;
                chart1.ChartAreas["Combined"].AxisY2.Minimum = 0.0;
                chart1.ChartAreas["Combined"].AxisY2.Maximum = 100.0;
                chart1.ChartAreas["Combined"].AxisY.MajorGrid.Enabled = false;
                Series combinedCDFSeries = chart1.Series.Add("Combined CDF");
                combinedCDFSeries.YAxisType = AxisType.Secondary;
                combinedCDFSeries.ChartType = SeriesChartType.Line;
                combinedCDFSeries.ChartArea = "Combined";
                combinedCDFSeries.Color = Color.Black;
                //chart1.ChartAreas["Combined PDF"].AxisX2.Title = "Travel Time: (min)\n\nCDF of Combined Travel Time Distribution";
                chart1.ChartAreas["Combined"].AxisY2.Title = "Cumulative\nPercentage";

                chart1.Legends.Add(new Legend("Cumulative Percentage"));
                combinedCDFSeries.Legend = "Cumulative Percentage";
                chart1.Legends["Cumulative Percentage"].DockedToChartArea = "Combined";
                chart1.Legends["Cumulative Percentage"].Docking = Docking.Top;
                chart1.Legends["Cumulative Percentage"].Alignment = StringAlignment.Near;
                combinedCDFSeries.BorderWidth = 3;

                double[] cdf = new double[10];
                combinedCDFSeries.Points.Add(new DataPoint(0, 0));
                for (int n = 0; n < 10; n++)
                {
                    if (n == 0)
                    {
                        cdf[n] = combinedPdf[n];
                    }
                    else
                    {
                        cdf[n] = cdf[n - 1] + combinedPdf[n];
                    }

                    combinedCDFSeries.Points.Add(new DataPoint(pdfScale[n], cdf[n] / combinedPdf.Sum() * 100));
                }

                chart1.ChartAreas.Add("CDFs");
                chart1.ChartAreas["CDFs"].AxisX.Minimum = 0;
                chart1.ChartAreas["CDFs"].AxisY.Minimum = 0;
                chart1.ChartAreas["CDFs"].AxisY.Maximum = 100;
                for (int scenario = 0; scenario < this.NumOfScenarios; scenario++)
                {
                    Series series = chart1.Series.Add(ScenarioNames[scenario] + "_CDF");
                    series.ChartType = SeriesChartType.Line;
                    series.ChartArea = "CDFs";

                    if (scenario == 0)
                    {
                        series.Color = Color.Green;
                    }

                    if (scenario == 1)
                    {
                        series.Color = Color.Yellow;
                    }

                    if (scenario == 2)
                    {
                        series.Color = Color.Red;
                    }

                    series.BorderWidth = 3;

                    cdf = new double[10];

                    series.Points.Add(0, 0);
                    for (int n = 0; n < 10; n++)
                    {
                        if (n == 0)
                        {
                            cdf[n] = pdfCounts[scenario][n];
                        }
                        else
                        {
                            cdf[n] = pdfCounts[scenario][n] + cdf[n - 1];
                        }

                        series.Points.Add(new DataPoint(pdfScale[n], cdf[n] / pdfCounts[scenario].Sum() * 100));
                    }

                    string legendName = string.Format("{0} CDF", this.ScenarioNames[scenario]);
                    chart1.Legends.Add(new Legend(legendName));
                    series.Legend = legendName;
                    chart1.Legends[legendName].DockedToChartArea = "CDFs";
                    chart1.Legends[legendName].Docking = Docking.Bottom;
                    chart1.Legends[legendName].Alignment = StringAlignment.Far;
                }

                double[] gps_cdf = new double[10];
                if (vehicleMap != null)
                {
                    string legendName = "GPS Trajectories_CDF";
                    
                    Series series = chart1.Series.Add(legendName);

                    chart1.Legends.Add(new Legend(legendName));
                    series.Legend = legendName;
                    chart1.Legends[legendName].DockedToChartArea = "CDFs";
                    series.ChartType = SeriesChartType.Line;
                    series.ChartArea = "CDFs";

                    chart1.Legends[legendName].Docking = Docking.Bottom;
                    chart1.Legends[legendName].Alignment = StringAlignment.Far;

                    series.Color = Color.Chocolate;
                    series.BorderWidth = 3;

                    series.Points.Add(new DataPoint(0, 0));

                    for (int i = 0; i < 10; i++)
                    {
                        if (i == 0)
                        {
                            gps_cdf[i] = gps_pdfCounts[i];
                        }
                        else
                        {
                            gps_cdf[i] = gps_pdfCounts[i] + gps_cdf[i - 1];
                        }

                        series.Points.Add(new DataPoint(pdfScale[i], gps_cdf[i] / gps_pdfCounts.Sum() * 100));
                    }

                }

                chart1.ChartAreas["CDFs"].AxisX.Title = "Travel Time: (min)\n\nScenario Specific Travel Time CDF";
                chart1.ChartAreas["CDFs"].AxisY.Title = "Cumulative\nPercentage";

                chart1.ChartAreas.Add("ScheduleDelay");

                chart1.ChartAreas["ScheduleDelay"].AxisX.Title = "Depature Time\n\nTime Dependent Scenario Specific Average Travel Time";
                chart1.ChartAreas["ScheduleDelay"].AxisY.Title = "Travel\nTime";

                Series dataSeries1 = chart1.Series.Add("PlanningTime");
                dataSeries1.ChartType = SeriesChartType.Spline;
                dataSeries1.ChartArea = "ScheduleDelay";
                dataSeries1.Color = Color.Blue;
                dataSeries1.XValueType = ChartValueType.Time;
                dataSeries1.BorderWidth = 3;
                dataSeries1.BorderDashStyle = ChartDashStyle.DashDot;

                chart1.Legends.Add(new Legend("Planning Time"));
                dataSeries1.Legend = "Planning Time";
                chart1.Legends["Planning Time"].DockedToChartArea = "ScheduleDelay";
                chart1.Legends["Planning Time"].Docking = Docking.Bottom;
                chart1.Legends["Planning Time"].Alignment = StringAlignment.Far;


                System.DateTime startingTime = new DateTime(2012, 7, 24, 0, 0, 0);
                chart1.ChartAreas["ScheduleDelay"].AxisX.Minimum = startingTime.ToOADate() - new DateTime(2012, 7, 24, 0, 0, 0).ToOADate();

                for (int scenario = 0; scenario < this.NumOfScenarios; scenario++)
                {
                    Series series = chart1.Series.Add(this.ScenarioNames[scenario] + "_TD_Average");
                    series.ChartType = SeriesChartType.Spline;
                    series.ChartArea = "ScheduleDelay";
                    series.XValueType = ChartValueType.Time;
                    if (scenario == 0)
                    {
                        series.Color = Color.Green;
                    }

                    if (scenario == 1)
                    {
                        series.Color = Color.Yellow;
                    }

                    if (scenario == 2)
                    {
                        series.Color = Color.Red;
                    }

                    series.BorderWidth = 3;
                    for (int departure_hour = this.AnalysisStartHour; departure_hour < AnalysisEndHour; departure_hour++)
                    {
                        if (odSummaryMap.ContainsKey(departure_hour))
                        {

                            ODPathSummary summary = odSummaryMap[departure_hour];
                            if (odSummaryMap.ContainsKey(departure_hour))
                            {
                                double travelTime = 0;
                                double counts = 0;
                                for (int pathIndex = 0; pathIndex < summary.pathList.Count; pathIndex++)
                                {
                                    if (summary.ScenarioBasedExperiencedPathTravelTimeMap[scenario].ContainsKey(pathIndex))
                                    {
                                        travelTime += summary.ScenarioBasedExperiencedPathTravelTimeMap[scenario][pathIndex].Sum();
                                        counts += summary.ScenarioBasedExperiencedPathTravelTimeMap[scenario][pathIndex].Count;
                                    }
                                }

                                if (counts > 0)
                                {
                                    double average = travelTime / counts;
                                    DateTime x = startingTime.AddHours(departure_hour);
                                    series.Points.AddXY(x, average);

                                    if (scenario == 0)
                                    {
                                        dataSeries1.Points.AddXY(x, average * 1.4);
                                    }
                                }
                            }
                        }


                    }

                    if (series.Points.Count <= 1)
                    {
                        series.ChartType = SeriesChartType.Point;
                    }

                    if (dataSeries1.Points.Count <= 1)
                    {
                        dataSeries1.ChartType = SeriesChartType.Point;
                    }

                    string legendName = string.Format("{0}_TD_Average", this.ScenarioNames[scenario]);
                    chart1.Legends.Add(new Legend(legendName));
                    series.Legend = legendName;
                    chart1.Legends[legendName].DockedToChartArea = "ScheduleDelay";
                    chart1.Legends[legendName].Docking = Docking.Bottom;
                    chart1.Legends[legendName].Alignment = StringAlignment.Far;

                }
            }
            else // only GPS data
            {
                chart1.ChartAreas.Clear();

                chart1.ChartAreas.Add("PDF");

                chart1.ChartAreas["PDF"].AxisX.Title = "Travel Time: (min)\n\nGPS Travel Time Histogram";
                chart1.ChartAreas["PDF"].AxisY.Title = "Frequency";

                chart1.Legends.Clear();

                double[] pdfScale = new double[10];


                double min = double.MaxValue, max = double.MinValue;

                if (vehicleMap != null)
                {
                    foreach (KeyValuePair<int, List<IVehicle>> kvp in vehicleMap)
                    {
                        foreach (IVehicle v in kvp.Value)
                        {
                            if (v.VehicleTravelTime > max)
                            {
                                max = v.VehicleTravelTime;
                            }

                            if (v.VehicleTravelTime < min)
                            {
                                min = v.VehicleTravelTime;
                            }
                        }
                    }
                }

                chart1.ChartAreas["PDF"].AxisX.Minimum = 0;

                double unit_value = (max - min) / 10;

                for (int i = 0; i < 10; i++)
                {
                    pdfScale[i] = min + i * unit_value;
                }

                chart1.ChartAreas.Add("CDFs");
                chart1.ChartAreas["CDFs"].AxisX.Minimum = 0;
                chart1.ChartAreas["CDFs"].AxisY.Minimum = 0;
                chart1.ChartAreas["CDFs"].AxisY.Maximum = 100;

                double[] gps_pdfCounts = new double[10];
                if (vehicleMap != null)
                {
                    
                    string legendName = "GPS Trajectories";
                    summaryText += legendName + "\n";

                    Series series = chart1.Series.Add(legendName);

                    chart1.Legends.Add(new Legend(legendName));
                    series.Legend = legendName;
                    chart1.Legends[legendName].DockedToChartArea = "PDF";
                    series.ChartType = SeriesChartType.Column;
                    series.ChartArea = "PDF";

                    chart1.Legends[legendName].Docking = Docking.Top;
                    chart1.Legends[legendName].Alignment = StringAlignment.Far;

                    series.Color = Color.Chocolate;
                    series.BorderWidth = 3;

                    Dictionary<int, List<float>> gpsTravelTimeMap = new Dictionary<int, List<float>>();

                    foreach (KeyValuePair<int, List<IVehicle>> kvp in vehicleMap)
                    {
                        foreach (IVehicle v in kvp.Value)
                        {
                            if (v.VehicleStartHour >= AnalysisStartHour && v.VehicleStartHour < AnalysisEndHour)
                            {
                                if (!gpsTravelTimeMap.ContainsKey(v.VehicleStartHour))
                                {
                                    gpsTravelTimeMap[v.VehicleStartHour] = new List<float>();
                                }

                                gpsTravelTimeMap[v.VehicleStartHour].Add(v.VehicleTravelTime);

                                int index;
                                if (unit_value < 0.000001)
                                {
                                    index = 0;
                                }
                                else
                                {
                                    index = (int)((v.VehicleTravelTime - min) / unit_value);
                                }                                

                                if (index >= 10)
                                {
                                    index = 9;
                                }
                                gps_pdfCounts[index]++;
                            }

                        }
                    }

                    for (int hour = AnalysisStartHour; hour < AnalysisEndHour; hour++)
                    {
                        if (gpsTravelTimeMap.ContainsKey(hour))
                        {
                            summaryText += hour.ToString() + "\n";
                            summaryText += string.Join(",", gpsTravelTimeMap[hour].ToArray()) + "\n";
                        }
                    }
                    summaryText += "\n";

                    for (int n = 0; n < 10; n++)
                    {
                        series.Points.Add(new DataPoint(pdfScale[n], gps_pdfCounts[n]));
                    }
                }

                double[] gps_cdf = new double[10];
                if (vehicleMap != null)
                {
                    string legendName = "GPS Trajectories_CDF";

                    Series series = chart1.Series.Add(legendName);

                    chart1.Legends.Add(new Legend(legendName));
                    series.Legend = legendName;
                    chart1.Legends[legendName].DockedToChartArea = "CDFs";
                    series.ChartType = SeriesChartType.Line;
                    series.ChartArea = "CDFs";

                    chart1.Legends[legendName].Docking = Docking.Bottom;
                    chart1.Legends[legendName].Alignment = StringAlignment.Far;

                    series.Color = Color.Chocolate;
                    series.BorderWidth = 3;

                    series.Points.Add(new DataPoint(0, 0));

                    for (int i = 0; i < 10; i++)
                    {
                        if (i == 0)
                        {
                            gps_cdf[i] = gps_pdfCounts[i];
                        }
                        else
                        {
                            gps_cdf[i] = gps_pdfCounts[i] + gps_cdf[i - 1];
                        }

                        series.Points.Add(new DataPoint(pdfScale[i], gps_cdf[i] / gps_pdfCounts.Sum() * 100));
                    }

                }

                chart1.ChartAreas["CDFs"].AxisX.Title = "Travel Time: (min)\n\nGPS Travel Time CDF";
                chart1.ChartAreas["CDFs"].AxisY.Title = "Cumulative\nPercentage";
            }

            string summaryFileName = parentForm.projectFolder + "\\DetailedODStatisticsData.csv";
            using (StreamWriter writer = new StreamWriter(summaryFileName))
            {
                writer.Write(summaryText);
            }
        }        

        private void ODStatisticsCharForm_SizeChanged(object sender, EventArgs e)
        {
            chart1.Location = new Point(0, 0);
            chart1.Size = this.ClientSize;
        }
    }
}
